/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.puc.executor;

import com.puc.GUI.Simulador2;
import com.puc.vo.Dispositivo;
import com.puc.vo.Operacao;
import com.puc.vo.ProcessoRR;
import com.puc.vo.Processo.ProcessoStatus;
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 *
 * @author FerstL
 */
public class EscalonadorRoundRobin implements Runnable{

    public static ProcessoRR CPU;
    public static PriorityQueue<ProcessoRR> listaProcessosProntos = new PriorityQueue<ProcessoRR>();
    public static PriorityQueue<ProcessoRR>listaProcessosBloqueados = new PriorityQueue<ProcessoRR>();
    public static PriorityQueue<ProcessoRR>listaProcessosExecutados = new PriorityQueue<ProcessoRR>();
    public static ProcessoRR processoTransicaoProntos;
    private static ProcessoRR processoTransicaoExecutados;
    public static ArrayList<ProcessoRR>listaProcessosCriados = new ArrayList<ProcessoRR>();
    public static List<Dispositivo> listaDispositivo = new ArrayList<Dispositivo>();
    public static int TEMPO = 4;
    private static int countInterecao = 0;
    private static int posicaoDelete = 0;
    public static boolean pause = false;
    private static double tempoCPUOciosa;
    private static double tempoCPUOcupada;


 /*
        Realiza o escalonamento da CPU com o algoritmo SJF e ao mesmo tempo os escalonemento dos dispositivos pelo algoritmo FIFO
 *
 */
    @Override
    public void run() {


             

		while (listaProcessosProntos.size() != 0 || listaProcessosBloqueados.size() !=0 || CPU != null || listaProcessosCriados.size() != 0){
		if (!pause){
                    System.out.println("Momento: " + countInterecao);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException ex) {
                        //Logger.getLogger(Simulador2.class.getName()).log(Level.SEVERE, null, ex);
                    }
                     // Realiza escalonamento dos dispositivos via FIFO.
                     for (int i = 0; i < listaDispositivo.size(); i++) {

                        Dispositivo dispositivo = listaDispositivo.get(i);
			if (countInterecao%dispositivo.getTempoOperacao() == 0){
                            if (!dispositivo.getListaProcessos().isEmpty()){

                                //Pega primeiro o primeiro processo esperando para executar
                                ProcessoRR processoDispositivo = (ProcessoRR)dispositivo.getListaProcessos().remove(0);
				processoDispositivo.setEstado(ProcessoStatus.PRONTO);
                                System.out.println("Processo: " + processoDispositivo.getId() + " Voltou para a Lista de prontos");
                                listaProcessosProntos.add(processoDispositivo);
                                processoTransicaoProntos = processoDispositivo;
				listaProcessosBloqueados.poll();

                                //###########Controle de tela###########
                                Simulador2.removeObjetoTabela2();
                                Thread tt = new Thread(new AddValueProntos() );
                                tt.start();

        		    }

			}
		      }

                      //passa processos para a lista de prontos
                      for (int i = 0; i < listaProcessosCriados.size();i++) {

                            ProcessoRR processo = listaProcessosCriados.get(i);
                            if (processo.getArrivalTime() == countInterecao){
                                processo.setArrivalTime(processo.getArrivalTime()+ countInterecao);
                                listaProcessosProntos.add(processo);
                                listaProcessosCriados.remove(i);
                                processoTransicaoProntos = processo;
                                Thread tt = new Thread(new AddValueProntos());
                                tt.start();
                                Simulador2.removeObjetoTabela5(i);


                            }
                      }
                      //Pega primeiro processo da lista de prontos se nao existir nenhum porcesso em execucao
                      ProcessoRR processo = (CPU == null)?listaProcessosProntos.poll():null;

                      if(CPU!= null){

                          // Verifica se eh hora de sair da CPU por tempo
                          if(CPU.getTempoProcessoCPU()%TEMPO == 0){

                                Thread tt2 = new Thread(new removeProcessosCPU());
                                tt2.start();
                                System.out.println("Processo: " + CPU.getId() + " Saiu da CPU");
                                //CPU.setProcessorTime(CPU.getProcessorTime()-TEMPO);
                                //CPU.setTempoProcessoCPU(0);
                                if(CPU.getProcessorTime() > 0){
                                    listaProcessosProntos.add(CPU);
                                    processoTransicaoProntos = CPU;
                                    Thread addingProntos = new Thread(new AddValueProntos());
                                    addingProntos.start();
                                    //processoTransicaoProntos = null;
                                }
                                CPU = null;
                                processo = listaProcessosProntos.poll();


                          }

                      }

                      if (listaProcessosProntos.size() > 0 || processo != null || CPU != null){
                            if (CPU == null && processo.getEstado() == ProcessoStatus.BLOQUEADO){
                                //adiona processo na CPU
                                Simulador2.AddProcessoCPU(processo);

                                //melhorar para procurar pelo proximo que nao esteja bloqueado
                            }

                              try {
                                 Thread.sleep(1000);
                                } catch (InterruptedException ex) {

                                }

                            if (CPU == null){

				processo.setEstado(ProcessoStatus.EXECUTANDO);
				//processo.setArrivalTime(countInterecao);
				System.out.println("ProcessoRR " + processo.getId() + " esta na CPU");

                                Simulador2.AddProcessoCPU(processo);
                                //posicaoDelete = procuraProcessoFila(processo);
                                Thread removeProntos = new Thread(new removeProcessosProntos());
                                removeProntos.start();
                                try {
                                    removeProntos.join();
                                 } catch (InterruptedException ex) {

                                 }

                                CPU = processo;
                            }

                        //verifica se o processo da CPU acabou
			if (CPU.getProcessorTime() == 0){
				System.out.println("ProcessoRR: " + CPU.getId() + "ACABOU");

                                Thread tt2 = new Thread(new removeProcessosCPU());
                                tt2.start();
                                listaProcessosExecutados.add(CPU);
                                processoTransicaoExecutados = CPU;
                                //Adiciona processo na lista de processados
                                Thread tt = new Thread(new AddValueExecutados());
                                tt.start();

                                CPU = null;

				//pode colocar ele na lista dos processos prontos
			}
                        //somente se tiver IO para fazer
                        if(CPU!= null && CPU.getIOPerations() != null && !(CPU.getIOPerations().isEmpty()))
                          {
                            for (int i = 0; i<CPU.getIOPerations().size();i++){
                                    if (((Operacao)CPU.getIOPerations().get(i)).getMomento() == countInterecao){

                                        CPU.setEstado(ProcessoStatus.BLOQUEADO);
                                        //seta Operacao de IO que esta bloqueando o processo
                                        CPU.setOperacaoBloqueadora((Operacao)CPU.getIOPerations().get(i));
                                        listaProcessosBloqueados.add(CPU);

                                         //Adiciona processo na tabela de bloqueados
                                         Simulador2.AddProcessoBloqueados(CPU);

                                        //((Dispositivo)listaDispositivo.get(0)).getListaProcessos().add(CPU);
                                        setCPUDispositivo(((Operacao)CPU.getIOPerations().get(i)).getDispositivo().getId(), CPU);
                                        System.out.println("Processo " + CPU.getId() + " foi para a lista de IO no Disposotivo: " + ((Operacao)CPU.getIOPerations().get(i)).getDispositivo().getId());

                                        CPU = null;

                                        //Remove processo da CPU
                                        //Simulador2.removeObjetoTabela4();
                                        Thread tt = new Thread(new removeProcessosCPU());
                                        tt.start();


                                        break;
                                    }
                                }
                        }

			}
			countInterecao++;
                        //Controles de Tela
                        Simulador2.momentoDisplay.setText(Integer.toString(countInterecao));
                        if (CPU != null){
                            CPU.setProcessorTime(CPU.getProcessorTime() -1);
                            CPU.setTempoProcessoCPU(CPU.getTempoProcessoCPU()+1);
                        }
                    } // fim if pause
                    incrementaTempoProntos();
                    incrementaTempoBloqueados();
                    incrementaTempoDispositivosOcupado();
                    if (CPU == null)
                         tempoCPUOciosa++;
                    else
                        tempoCPUOcupada++;



		}

              Simulador2.jButton1.setEnabled(true);
              Simulador2.jButton4.setEnabled(true);
              Simulador2.habilitarCampos();
              Simulador2.gerarRelatorio.setEnabled(true);
    }

    public static class AddValueExecutados implements Runnable{

        @Override
        public void run() {

             Simulador2.AddValueExecutados(processoTransicaoExecutados);
             processoTransicaoExecutados = null;

        }


}
public static class AddValueProntos implements Runnable{

        @Override
        public void run() {

             Simulador2.AddValueProntos(processoTransicaoProntos);
             processoTransicaoProntos = null;
}
}
public static class AddValueProcessos implements Runnable{

        @Override
        public void run() {

            Simulador2.AddValueProcessos(listaProcessosProntos, listaProcessosCriados);

}
}
public static class removeProcessosProntos implements Runnable{

        @Override
        public void run() {

          Simulador2.removeObjetoTabela1(posicaoDelete);

           }

}
private void setCPUDispositivo(String ID, ProcessoRR CPU){

    for (int i=0; i<listaDispositivo.size(); i++) {
        //Dispositivo dispositivo = it.next();
        String DispId = listaDispositivo.get(i).getId();
        if (DispId.equals(ID))
            listaDispositivo.get(i).getListaProcessos().add(CPU);
    }

}

public static class removeProcessosCPU implements Runnable{

        @Override
        public void run() {

            Simulador2.removeObjetoTabela4();

           }

}

public static void resetaEscalonador(){

    CPU = null;
    listaProcessosProntos = new PriorityQueue<ProcessoRR>();
    listaProcessosBloqueados = new PriorityQueue<ProcessoRR>();
    listaProcessosExecutados = new PriorityQueue<ProcessoRR>();
    processoTransicaoProntos = null;
    listaProcessosCriados = new ArrayList<ProcessoRR>();
    listaDispositivo = new ArrayList<Dispositivo>();
    countInterecao = 0;


}
private void incrementaTempoProntos(){

        Object[] listaProntos = listaProcessosProntos.toArray();
        for(int i = 0; i<listaProntos.length; i++){
            ((ProcessoRR)listaProntos[i]).setWatingTime(((ProcessoRR)listaProntos[i]).getWatingTime() + 1);
        }

}
private void incrementaTempoBloqueados(){

        Object[] listaBloqueados = listaProcessosBloqueados.toArray();
        for(int i = 0; i<listaBloqueados.length; i++){
            ((ProcessoRR)listaBloqueados[i]).setBlockedTime(((ProcessoRR)listaBloqueados[i]).getBlockedTime() + 1);
        }

}
private void incrementaTempoDispositivosOcupado(){


        for(int i = 0; i<listaDispositivo.size(); i++){
            // Se a cada cada interacao o dispositivo tiver processos pendentes, ou seja na lista de processos, adiciona 1
            if(listaDispositivo.get(i).getListaProcessos().size() > 0)
                listaDispositivo.get(i).setTempoOcupado(listaDispositivo.get(i).getTempoOcupado()+1);
        }

}
private int procuraProcessoFila(ProcessoRR processo){

    Object[] procuraListaProcessosProntos = listaProcessosProntos.toArray();
    int contador = 0;
    for(int i = 0; i < procuraListaProcessosProntos.length; i++){

        if (processo.getId().equals(((ProcessoRR)procuraListaProcessosProntos[i]).getId()))
            return contador;
        else
            contador++;

    }

    return contador;
}

public static int getCountInterecao() {
        return countInterecao;
}

    public static double getTempoCPUOciosa() {
        return tempoCPUOciosa;
    }

    public static double getTempoCPUOcupada() {
        return tempoCPUOcupada;
    }


}


